#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <cstring>
#include <cstdlib>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include <unordered_map>
#include <pthread.h>
#include <vector>
#include "err.hpp"
#include "RingQueue.hpp"
#include "lockGuard.hpp"
#include "thread.hpp"


using namespace std;

namespace ns_server{

    const static uint16_t default_port = 8080;

    //一个参数为string，返回值为string的函数指针func_t
    using func_t = function<string(string)>;

    class UdpServer{
    public:

        // UdpServer(string ip, uint16_t port = default_port): _port(port), _ip(ip){
        //     cout << "server addr: " << _ip << " " << _port << endl;
        // }

        //云服务器去掉ip地址
        UdpServer(uint16_t port = default_port): _port(port) {
            cout << "server addr: " << _port << endl;
            pthread_mutex_init(&_lock, nullptr);
            //把Recv和this包装一下，一起传入func_t func
            p = new Thread(1, std::bind(&UdpServer::Recv, this));
            c = new Thread(2, std::bind(&UdpServer::broadCast, this));
        }

        ~UdpServer(){
            pthread_mutex_destroy(&_lock);
            p->join();
            c->join();
            delete c;
            delete p;
        }

        //初始化+运行cp
        void Start(){

            //1、创建socket接口，打开网络文件
            _sock = socket(AF_INET, SOCK_DGRAM, 0);
            if(_sock<0){
                cerr << "create socket error: " << strerror(errno) << endl;
                exit(SOCKET_ERR);
            }
            cout << "create socket success: " << _sock << endl;

            //2、给服务器指明IP和Port，绑定
            // 创建并初始化sockaddr结构，用于存放服务器相关信息
            struct sockaddr_in local;
            //local置零，清空
            bzero(&local, sizeof(local));

            // 设置域
            local.sin_family = AF_INET;

            // 设置端口号，本机的端口号需要被目的主机所接收 -- 主机序列需要被转换成网络序列
            local.sin_port = htons(_port);

            //2.1、把string转成uint32_t
            //2.2、将主机ip转成网络序列
            // inet_addr把这两件事同时完成了
            //2.3、云服务器或一款服务器，一般不要指明某个确定的ip（服务器可能有多个网卡）
            // local.sin_addr.s_addr = inet_addr(_ip.c_str());
            // 让server在启动的时候bind本主机上的任意ip
            local.sin_addr.s_addr = INADDR_ANY;

            //将local绑定到OS内核，我们前面定义的是sockaddr_in，这里需要强转
            if(bind(_sock, (struct sockaddr *)&local, sizeof(local)) < 0){
                cerr<<"bind socket error: "<< strerror(errno) << endl;
                exit(BIND_ERR);
            }
            cout << "bind success." << endl;

            p->run();
            c->run();
        }

        //收+业务处理
        void Recv(){
            while(1){
                //收
                //远端套接字，用于存放从客户端相关信息
                struct sockaddr_in peer;
                //peer置零，清空
                bzero(&peer, sizeof(peer));
                socklen_t len = sizeof(peer);
                char buf[1024];
                int n = recvfrom(_sock, buf, sizeof(buf)-1, 0, (struct sockaddr*)&peer, &len);
                if(n>0){
                    buf[n] = '\0';
                }else{
                    continue;
                }

                //提取客户端信息
                //获取客户端ip
                string clientip = inet_ntoa(peer.sin_addr);
                //获取客户端端口号
                uint16_t clientport = ntohs(peer.sin_port);
                cout << clientip << " - " << clientport << "  get a message# " << buf << endl;

                // //业务处理
                // string message = _service(buf);
                //把获取到用户放入unordered_map
                //构建一个用户并检查
                string name = clientip;
                name += "-";
                name += to_string(clientport);
                //把不存在的用户插入
                addUser(name, peer);

                string message = name + ">> " + buf;

                //把用户的消息存到消息队列中
                _rq.push(message);



                // //发
                // sendto(_sock, message.c_str(), message.size(), 0, (struct sockaddr *)&peer, sizeof(peer));
            }
        }

        //添加用户
        void addUser(const string &name, const struct sockaddr_in &peer){

            LockGuard lg(&_lock);

            //检查是否存在
            // //unordered_map自带去重
            // onlineuser[name] = peer;
            auto iter = onlineuser.find(name);
            if(iter == onlineuser.end()){
                onlineuser.insert(make_pair(name, peer));
            }
            
        }

        //给所有用户发消息
        void broadCast(){
            while(1){
                string sendstr;
                _rq.pop(&sendstr);


                // //发的时候不能添加新用户 -- 不是最优方式
                // //  优化：拷贝一份onlineuser_cp，发的时候就用cp发，这样原onlineuser就能改变
                // LockGuard lg(&_lock);

                // for (auto user : onlineuser)
                // {
                //     //onlineuser(clientip-clientport, peer)
                //     cout << "broadcast message to " << user.first << sendstr << endl;
                //     //发
                //     sendto(_sock, sendstr.c_str(), sendstr.size(), 0, (struct sockaddr *)&user.second, sizeof(user.second));
                // }

                //把onlineuser中的peer拷贝到vector中
                vector<struct sockaddr_in> v;
                {
                    LockGuard lg(&_lock);
                    for(auto& user : onlineuser){
                        v.push_back(user.second);
                    }
                }

                for(auto& user : v){
                    //发
                    sendto(_sock, sendstr.c_str(), sendstr.size(), 0, (struct sockaddr *)&user, sizeof(user));
                }
            }

        }

    private:
        int _sock; // 创建套接字返回的文件描述符
        uint16_t _port; //网络端口号
        //string _ip; //ip地址，云服务器不允许用户手动绑定ip，后面要去掉

        // //一个func_t 类型的函数指针
        // func_t _service;

        //用户集
        unordered_map<string, struct sockaddr_in> onlineuser;

        //消息列表
        RingQueue<string> _rq;

        pthread_mutex_t _lock;

        Thread *c;
        Thread *p;
    };
}